package cn.kuporo.seckill.stock.application.service.impl;

import cn.kuporo.seckill.common.cache.distribute.DistributedCacheService;
import cn.kuporo.seckill.common.cache.model.SeckillBusinessCache;
import cn.kuporo.seckill.common.constants.MQConstants;
import cn.kuporo.seckill.common.constants.RedisConstants;
import cn.kuporo.seckill.common.exception.ErrorCode;
import cn.kuporo.seckill.common.exception.SeckillException;
import cn.kuporo.seckill.common.lock.DistributedLock;
import cn.kuporo.seckill.common.lock.factory.DistributedLockFactory;
import cn.kuporo.seckill.common.model.dto.SeckillStockDTO;
import cn.kuporo.seckill.common.model.message.ErrorMessage;
import cn.kuporo.seckill.common.model.message.TxMessage;
import cn.kuporo.seckill.mq.MessageSenderService;
import cn.kuporo.seckill.stock.application.cache.SeckillStockBucketCacheService;
import cn.kuporo.seckill.stock.application.model.command.SeckillStockBucketWrapperCommand;
import cn.kuporo.seckill.stock.application.model.dto.SeckillStockBucketDTO;
import cn.kuporo.seckill.stock.application.service.SeckillStockBucketArrangementService;
import cn.kuporo.seckill.stock.application.service.SeckillStockBucketService;
import cn.kuporo.seckill.stock.domain.model.dto.SeckillStockBucketDeduction;
import cn.kuporo.seckill.stock.domain.service.SeckillStockBucketDomainService;
import com.alibaba.fastjson.JSON;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

/**
 * @author: kuporo
 * @since: 2025/8/2
 * @description: 分桶库存服务
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SeckillStockBucketServiceImpl implements SeckillStockBucketService {
    private final DistributedLockFactory distributedLockFactory;
    private final SeckillStockBucketArrangementService seckillStockBucketArrangementService;
    private final SeckillStockBucketCacheService seckillStockBucketCacheService;
    private final SeckillStockBucketDomainService seckillStockBucketDomainService;
    private final DistributedCacheService distributedCacheService;
    private final MessageSenderService messageSenderService;
    @Override
    public void arrangeStockBuckets(Long userId, SeckillStockBucketWrapperCommand stockBucketWrapperCommand) {
        if (userId == null || stockBucketWrapperCommand == null) {
            throw new SeckillException(ErrorCode.PARAMS_INVALID);
        }
        stockBucketWrapperCommand.setUserId(userId);
        if (stockBucketWrapperCommand.isEmpty()){
            throw new SeckillException(ErrorCode.PARAMS_INVALID);
        }
        log.info("arrangeBuckets|编排库存分桶|{}", JSON.toJSON(stockBucketWrapperCommand));
        String lockKey = RedisConstants.getKey(RedisConstants.getKey(RedisConstants.GOODS_BUCKET_ARRANGEMENT_KEY, stockBucketWrapperCommand.getUserId()),stockBucketWrapperCommand.getGoodsId());
        DistributedLock lock = distributedLockFactory.getDistributedLock(lockKey);
        try{
            boolean isLock = lock.tryLock();
            if (!isLock){
                throw new SeckillException(ErrorCode.FREQUENTLY_ERROR);
            }
            //获取到锁，编排库存
            seckillStockBucketArrangementService.arrangeStockBuckets(stockBucketWrapperCommand.getGoodsId(),
                    stockBucketWrapperCommand.getStockBucketCommand().getTotalStock(),
                    stockBucketWrapperCommand.getStockBucketCommand().getBucketsQuantity(),
                    stockBucketWrapperCommand.getStockBucketCommand().getArrangementMode());
            log.info("arrangeStockBuckets|库存编排完成|{}", stockBucketWrapperCommand.getGoodsId());
        }catch (SeckillException e){
            log.error("arrangeStockBuckets|库存编排失败|{}", stockBucketWrapperCommand.getGoodsId(), e);
            throw e;
        }catch (Exception e){
            log.error("arrangeStockBuckets|库存编排错误|{}", stockBucketWrapperCommand.getGoodsId(), e);
            throw new SeckillException(ErrorCode.BUCKET_CREATE_FAILED);
        }finally {
            lock.unlock();
        }
    }

    @Override
    public SeckillStockBucketDTO getTotalStockBuckets(Long goodsId, Long version) {
        if (goodsId == null){
            throw new SeckillException(ErrorCode.PARAMS_INVALID);
        }
        log.info("stockBucketsSummary|获取库存分桶数据|{}", goodsId);
        return seckillStockBucketArrangementService.getSeckillStockBucketDTO(goodsId, version);
    }

    @Override
    public SeckillBusinessCache<Integer> getAvailableStock(Long goodsId, Long version) {
        if (goodsId == null){
            throw new SeckillException(ErrorCode.PARAMS_INVALID);
        }
        return seckillStockBucketCacheService.getAvailableStock(goodsId, version);
    }

    @Override
    public SeckillBusinessCache<SeckillStockDTO> getSeckillStock(Long goodsId, Long version) {
        if (goodsId == null){
            throw new SeckillException(ErrorCode.PARAMS_INVALID);
        }
        return seckillStockBucketCacheService.getSeckillStock(goodsId, version);
    }

    @Override
    @Transactional
    public boolean decreaseStock(TxMessage txMessage) {
        Boolean decrementStock = distributedCacheService.hasKey(RedisConstants.getKey(RedisConstants.STOCK_TX_KEY, txMessage.getTxNo()));
        if (Boolean.TRUE.equals(decrementStock)){
            log.info("updateAvailableStock|秒杀商品微服务已经扣减过库存|{}", txMessage.getTxNo());
            return true;
        }
        boolean isUpdate = false;
        try {
            isUpdate = seckillStockBucketDomainService.decreaseStock(new SeckillStockBucketDeduction(txMessage.getGoodsId(), txMessage.getQuantity(), txMessage.getUserId(), txMessage.getBucketSerialNo()));
            // 成功扣减库存成功
            if (isUpdate){
                distributedCacheService.put(RedisConstants.getKey(RedisConstants.STOCK_TX_KEY, txMessage.getTxNo()), txMessage.getTxNo(), RedisConstants.TX_LOG_EXPIRE_DAY, TimeUnit.DAYS);
            }else{
                // 发送失败消息给订单微服务
                messageSenderService.send(getErrorMessage(txMessage));
            }
        } catch (Exception e) {
            isUpdate = false;
            log.error("decreaseStock|抛出异常|{}|{}",txMessage.getTxNo(), e.getMessage());
            //发送失败消息给订单微服务
            messageSenderService.send(getErrorMessage(txMessage));
        }
        return isUpdate;
    }
    /**
     * 发送给订单微服务的错误消息
     */
    private ErrorMessage getErrorMessage(TxMessage txMessage){
        return new ErrorMessage(MQConstants.TOPIC_ERROR_MSG, txMessage.getTxNo(), txMessage.getGoodsId(), txMessage.getQuantity(), txMessage.getPlaceOrderType(), txMessage.getException(), txMessage.getBucketSerialNo(), txMessage.getUserId(), txMessage.getOrderTaskId());
        
    }
}
